﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace HWR_Ants.Classes
{
    public class Map
    {
        private Options options = Tools.Tools.ActiveCurrentOptions;
        public readonly double ALPHA = Tools.Tools.ActiveCurrentOptions.Parameter.Alpha;
        public readonly double BETA = Tools.Tools.ActiveCurrentOptions.Parameter.Beta;
        public readonly double INITIAL_PHEROMON = Tools.Tools.ActiveCurrentOptions.Parameter.Tau;
        public readonly double EVAPOTRANSPIRATION_FACTOR = Tools.Tools.ActiveCurrentOptions.Parameter.Rho;
        public readonly double Q = Tools.Tools.ActiveCurrentOptions.Parameter.Q;

        public readonly int MAX_ITERATIONS = Tools.Tools.ActiveCurrentOptions.IterationCounter;
        public readonly int NUMBEROFANTS = Tools.Tools.ActiveCurrentOptions.AntsCounter;

        public delegate void UpdateEventHandler(Map sender, UpdateEventArgs e);
        public event UpdateEventHandler UpdateEvent;

        private static readonly Random _Random = new Random();
        private List<City> _Cities;
        private List<Road> _Roads;
        private double _WorstTourValue;
        private double _WeightingFactor;
        int iterations = 0;
        private bool breakWhileLoop = false;

        public Map(MapWrapper mw) : this(mw.Cities, mw.Roads)
        {

        }

        public Map(IEnumerable<City> cities, IEnumerable<Road> roads)
        {
            _Cities = new List<City>(cities);
            _Roads = new List<Road>(roads);
            _WorstTourValue = 0;
            foreach (Road road in roads)
            {
                _WorstTourValue += road.Distance; 
            }
            int roadsCount = 1;
            if (!(_Roads.Count == 0))
                roadsCount = _Roads.Count;

            _WeightingFactor = _Cities.Count / roadsCount;
        }

        public IList<City> Cities
        {
            get { return _Cities; }
        }

        public IEnumerable<Road> Roads
        {
            get { return _Roads; }
        }

        public IEnumerable<City> FindTour()
        {
            IList<City> best_Tour = null;
            double best_tour_value = _WorstTourValue + 0.2;
            double current_bestTourValue = best_tour_value;
            int iterations_without_change = 0;
            int number_of_failures = 0;
            double last_value = 0;
            int num_success = 0;

            foreach (Road road in Roads)
            {
                road.PheromonValue = INITIAL_PHEROMON;
            }

            double ant_pheromone_capacity = Q;
            double overall_decay_value = EVAPOTRANSPIRATION_FACTOR * INITIAL_PHEROMON * _Roads.Count;

            // solange iteration nicht durchgelaufen und  sich noch was ändert
            while (     options.Stopp.ToLower() == "iteration"       && iterations < MAX_ITERATIONS 
                    ||  options.Stopp.ToLower() == "lösung gefunden" && iterations_without_change < (NUMBEROFANTS)
                    ||  options.Stopp.ToLower() == "schwellenwert"   && options.Schwellenwert <= best_tour_value )
            {
                // break IT ?
                if (breakWhileLoop)
                    break;

                current_bestTourValue = _WorstTourValue + 0.2;

                iterations++;
                List<Ant> ants = new List<Ant>(NUMBEROFANTS);
                Dictionary<Ant, bool> ant_success = new Dictionary<Ant, bool>(NUMBEROFANTS);

                for (int i = 1; i <= NUMBEROFANTS; i++)
                {
                    int rnd_index = _Random.Next(Cities.Count);
                    ants.Add(new Ant(Cities.ElementAt(rnd_index), ant_pheromone_capacity));
                }

                last_value = 0;
                num_success = 0;

                foreach (Ant ant in ants)
                {
                    bool success = (ant.SearchTour() && ant.VisitedCities.Count == _Cities.Count);
                    ant_success[ant] = success;

                    // break IT?
                    if (breakWhileLoop)
                        break;

                    if (success)
                    {
                        double delta = ant.TourValue - best_tour_value;
                        if (Math.Abs(delta) <= 0.01)
                        {
                            iterations_without_change++;
                        }
                        else if (delta < 0)
                         {
                            best_tour_value = ant.TourValue;
                            best_Tour = ant.VisitedCities;
                            iterations_without_change = 0;
                        }
                        else if (delta <= best_tour_value * 0.01)
                        {
                            iterations_without_change++;
                        }
                        else
                        {
                            iterations_without_change = 0;
                        }

                        if (current_bestTourValue > ant.TourValue)
                            current_bestTourValue = ant.TourValue;

                        last_value += ant.TourValue;
                        num_success++;
                    }
                    else
                    {
                        iterations_without_change = 0;
                        number_of_failures++; 
                    }
                }

                last_value /= num_success;

                foreach (Road road in Roads)
                {
                    bool road_is_in_best_tour = false;

                    // break IT?
                    if (breakWhileLoop)
                        break;

                    if (best_Tour != null && best_Tour.Count > 0)
                    {
                        City first = best_Tour.ElementAt(0);
                        for (int i = 1; i < best_Tour.Count - 1; i++)
                        {
                            if (first.Roads(best_Tour.ElementAt(i)) == road)
                            {
                                road_is_in_best_tour = true;
                                break;
                            }
                            first = best_Tour.ElementAt(i);
                        }

                        if (best_Tour.ElementAt(best_Tour.Count - 1).Roads(best_Tour.ElementAt(0)) == road)
                        {
                            road_is_in_best_tour = true;
                        }
                    }

                    if (!road_is_in_best_tour)
                    {
                        UpdatePheromoneLevel(road);
                    }
                }

                double individual_pheromone_level = overall_decay_value;

                foreach (KeyValuePair<Ant, bool> annotated_ant in ant_success)
                {
                    // break IT?
                    if (breakWhileLoop)
                        break;

                    if (annotated_ant.Value)
                    {
                        annotated_ant.Key.Pheromones = individual_pheromone_level;
                        IList<City> cities = annotated_ant.Key.VisitedCities;
                        double tour_bonus = TourPheromoneBonus(annotated_ant.Key);
                        for (int i = 1; i < cities.Count - 1; i++)
                        {
                            Road road = cities.ElementAt(i - 1).Roads(cities.ElementAt(i));
                            road.PheromonValue += tour_bonus;
                        }

                        Road last_road = cities.ElementAt(cities.Count - 1).Roads(cities.ElementAt(0));
                        last_road.PheromonValue += tour_bonus;
                    }
                }

                RaiseUpdate(new UpdateEventArgs(iterations, iterations_without_change, number_of_failures, best_tour_value,current_bestTourValue, last_value, best_Tour));
                
                if (iterations_without_change >= NUMBEROFANTS)
                    breakWhileLoop = true;

                System.Threading.Thread.Sleep(10);
            }

            return best_Tour;
        }

        private void RaiseUpdate(UpdateEventArgs e)
        {
            // System.Threading.Thread.Sleep(500);
            UpdateEvent(this, e);
        }

        private void UpdatePheromoneLevel(Road road)
        {
            double RemainingPheromoneFactor = (1.0 - EVAPOTRANSPIRATION_FACTOR);
            road.PheromonValue = road.PheromonValue * RemainingPheromoneFactor;
        }

        private double TourPheromoneBonus(Ant ant)
        {
            // TODO: Update durchdenken
            // return Math.Pow((ant.Pheromones * (_WorstTourValue / ant.TourValue - 1)), 2);
            return (ant.Pheromones / ant.TourValue);
        }

        public void Stop()
        {
            breakWhileLoop = true;
        }
    }
}
